/*
 * Bits.cpp
 *
 * Esta classe implementa a interface Bits.h que representa um conjunto de bits de tamanho fixo.
 * Os bits são indexados por um valor não-negativo e podem ser consultados, copiados e alterados.
 *
 *  Created on: 10/06/2011
 *      Authors: Erick e Glauco
 */

//includes
#include <stdlib.h>
#include "Bits.h"
#include <iostream>
#include <cstring>

/**
 * Construtor: Bits()
 * Este construtor aloca um conjunto de bits de acordo com o bitCount
 */
Bits::Bits(int bitCount) {
    bit_count = bitCount;

    // E.g, ceil(257/32*8) = 65, 64 ints fully used, last one partially used
    palavra = (unsigned int*) calloc(1, ((int) ceil(bitCount / SZ_UINT * 8)));
}

/**
 * Destrutor: ~Bits()
 * Destroi o conjunto de bits
 */
Bits::~Bits() {
    if (palavra)
        free(palavra);
}

Bits::Bits(const char* tostr) {

		int l = strlen(tostr);
		int bitCount = l * 8;
		bit_count = bitCount;
		// E.g, ceil(257/32*8) = 65, 64 ints fully used, last one partially used
		palavra = (unsigned int*) calloc(1, ((int) ceil(bitCount / SZ_UINT * 8)));

		for (int i = 0; i < l; i++) {

			int mask = 0x80;
			this->set(i*8, tostr[i] & mask);
			mask = mask >> 1;
			this->set(i*8+1, tostr[i] & mask);
			mask = mask >> 1;
			this->set(i*8+2, tostr[i] & mask);
			mask = mask >> 1;
			this->set(i*8+3, tostr[i] & mask);
			mask = mask >> 1;
			this->set(i*8+4, tostr[i] & mask);
			mask = mask >> 1;
			this->set(i*8+5, tostr[i] & mask);
			mask = mask >> 1;
			this->set(i*8+6, tostr[i] & mask);
			mask = mask >> 1;
			this->set(i*8+7, tostr[i] & mask);
		}
}

/**
 * informa o numero de bits encapsulado
 */
int Bits::getCount(){
	return this->bit_count;
}

/*
 * Função: set()
 * Está função seta o bit para 1 no indice correspondente.
 * @return 0 se bem sucedida, -1 se erro.
 **/
int Bits::set(int indice) {
    // Checa o valor do indice
    if (indice >= bit_count || !palavra)
        return -1;

    // O indice correto dentro do vetor palavra sera dado por indice/SZ_UNIT.
    // O indice do bit do vetor palavra é dado por indice%SZ_UNIT
    // para alcançar isto desloca-se para a esquerda 0x01 o numero de vezes apropriado
    palavra[indice / SZ_UINT] |= (0x00000001 << (indice % (SZ_UINT * 8) - 1));
    return 0;
}

/*
 * Função: reset()
 * Esta função Desativa o bit no índice especificado.
 * @return 0 on success, -1 on error.
 **/
int Bits::reset(int indice) {
    if (indice >= bit_count || !palavra)
        return -1;
    palavra[indice / SZ_UINT] &= ~(0x00000001 << (indice % (SZ_UINT * 8) - 1));
    return 0;
}

/*
 * Função: get()
 * Esta funçao retorna o valor do bit no indice correspondente
 * @return O valor do bit se o campo está inicializado e -1 caso contrário
 **/
int Bits::get(int indice) {
    if (indice >= bit_count || !palavra)
        return -1;
    return (palavra[indice / SZ_UINT] & (0x00000001 << (indice % (SZ_UINT * 8) - 1)) ? 1 : 0);
}

int Bits::length() {
    return bit_count;
}

int Bits::putInfo(int inicio, int tam, int info) {
//    if (tam > 32 || tam < 0)
//        return -1;

    for (int i = inicio + tam - 1; i >= inicio; --i) {
        if ((info & 0x01) != 0)
            set(i);
        else
            reset(i);

        info = info >> 1;
    }

}

/*
 * Função: set()
 * Está função seta o bit para o valor passado como parametro no indice correspondente.
 * @return 0 se bem sucedida, -1 se erro.
 **/
void Bits::set(int indice, int valor){
	if (valor)
		Bits::set(indice);
	else
		Bits::reset(indice);
}

int Bits::takeInfo(int inicio, int tam) {
//    if (tam > 32 || tam < 0)
//        return -1;

    int result = 0;

    for (int i = 0; i < tam; ++i) {
        result <<= 1;
        if (get(inicio + i) == true)
            result |= 0x01;
    }

    return result;
}

Bits* Bits::takeBits(int inicio, int tam) {
    if (inicio < 0 || inicio > this->length() - tam)
        return NULL;
    if (tam < 0)
        return NULL;

    Bits* valor = new Bits(tam);

    for (int i = 0; i < tam; i++)
        if (get(inicio + i))
            valor->set(i);

    return valor;
}

void Bits::println() {
	int a = 0;
    for (int i = 0; i < bit_count; i++) {
        std::cout << get(i);
        a++;
        if(a == 8){
        	std::cout << "||";
        	a = 0;
        }

    }
    std::cout << std::endl;
}


void Bits::copyBits(Bits *src, int offset, int len, int start) {
    //            if (offset < 0) {
    //                throw ("offset nao pode ser negativo");
    //            }
    //            if (len < 0) {
    //                throw ("len nao pode ser negativo");
    //            }
    //            if (start < 0) {
    //                throw ("start nao pode ser negativo");
    //            }
    //            if (offset + len > src->length()) {
    //                throw ("offset + len maior que src->length");
    //            }
    //            if (start + len > this->length()) {
    //                throw ("start + len maior que this->length");
    //            }

//    int tmp = src->takeInfo(offset, len);
//    printf("tmp:");
//    printf("%d", tmp);
//    this->putInfo(start, len, tmp);

	 for(int i = 0; i < len; i++){
		if(src->get(i+offset))
			this->set(i + start);
		else
			this->reset(i + start);
	 }

}

unsigned int Bits::toCharArray(unsigned char** stringd) {

    std::cout << "toCharArray" << std::endl;
    unsigned int tam = (unsigned int) ceil((float) this->bit_count / 8);
    std::cout << tam << std::endl;
    *stringd = new unsigned char[tam];
    memset(*stringd, 0, tam);
    //memcpy(*buffer, this->palavra, tam);
    int mask = 0x80;

    /*for (int i = 0; i < tam; i++) {
	
	*buffer[i] |= (this->get(i * 8 + 7) << 8);
	*buffer[i] |= (this->get(i * 8 + 6) << 7);
	*buffer[i] |= (this->get(i * 8 + 5) << 5);
	*buffer[i] |= (this->get(i * 8 + 4) << 4);
	*buffer[i] |= (this->get(i * 8 + 3) << 3);
	*buffer[i] |= (this->get(i * 8 + 2) << 2);
	*buffer[i] |= (this->get(i * 8 + 1) << 1);
	*buffer[i] |= (this->get(i * 8) << 0);
    }*/

    Bits* b = this;
    memset(*stringd, 0, tam);

        for (int i = 0; i < tam; ++i) {
                int k = i * 8;
                *stringd[i] |= (b->get(k) << 7);
                *stringd[i] |= (b->get(k + 1) << 6);
                *stringd[i] |= (b->get(k + 2) << 5);
                *stringd[i] |= (b->get(k + 3) << 4);
                *stringd[i] |= (b->get(k + 4) << 3);
                *stringd[i] |= (b->get(k + 5) << 2);
                *stringd[i] |= (b->get(k + 6) << 1);
                *stringd[i] |= (b->get(k + 7) << 0);
        }

    return tam;
}
